﻿using System;
using System.Data;
using System.Linq;
using System.Text;
using Medianamik.Core.Configuration;
using System.IO;
using System.Configuration;
using System.Reflection;
using SubSonic;
using SubSonic.Utilities;

namespace Medianamik.Core.CodeGeneration
{
    public class CodeGenerator
    {
        private static CodeGenerationConfigElement _config;
        protected static CodeGenerationConfigElement Config
        {
            get
            {
                if (_config == null)
                {
                    var m = (MedianamikConfigSection)ConfigurationManager.GetSection(ConfigurationSectionName.MEDIANAMIK);
                    _config = m.CodeGeneration;
                }

                return _config;
            }
        }

        public string ProviderName
        {
            get
            {
                return Config.ProviderName;
            }
        }

        private Assembly _executingAssembly;
        internal Assembly ExecutingAssembly
        {
            get
            {
                if (_executingAssembly == null)
                {
                    _executingAssembly = Assembly.GetExecutingAssembly();
                }
                return _executingAssembly;
            }
        }

        private DataProvider _provider;
        public DataProvider Provider
        {
            get
            {
                if (_provider == null)
                {
                    //Reset Providers
                    DataService.Provider = null;
                    DataService.Providers = null;
                    _provider = DataService.GetInstance(ProviderName);
                    PrepareProvider();
                }
                return _provider;
            }
        }

        private ICodeLanguage _language;
        public ICodeLanguage Language
        {
            get
            {
                if (_language == null)
                {
                    _language = CodeLanguageFactory.GetByShortName("cs");
                }
                return _language;
            }
        }

        /// <summary>
        /// Définit certaines valeurs par défaut pour le provider
        /// </summary>
        private void PrepareProvider()
        {
            _provider.StripViewText = "vw_gen_";
            _provider.ViewStartsWith = "vw_gen_";
            _provider.GeneratedNamespace = Config.Namespace;
            _provider.FixPluralClassNames = false; /* !Important , sinon la compilation des classes générées risque de ne plus fonctionner*/
        }

        public string RunConcatenated()
        {
            var code = Run(null, false);
            code.Append(Language.DefaultUsingStatements);
            code.Append(ConfigurationPropertyDefaultValue.CUSTOM_USING_STATEMENTS);

            return code.ToString();
        }

        public string RunAndWriteFiles(string pAbsoluteOutputPath)
        {
            return Run(pAbsoluteOutputPath, true).ToString();
        }

        //ICI on gégére l'api - joindre
        private StringBuilder Run(string pAbsoluteOutputPath, bool addUsings)
        {
            var writeFiles = !string.IsNullOrEmpty(pAbsoluteOutputPath);

            if (Provider == null)
            {
                throw new CoreException("Unable to find CodeGenerationProvider. Check your web.Config File.");
            }

            var compiler = new TurboCompiler();
            var viewNames = Provider.GetViewNameList().ToList().FindAll(vw => vw.StartsWith("vw_gen_"));

            foreach (var viewName in viewNames)
            {
                var className = DataService.GetTableSchema(viewName, ProviderName, TableType.View).ClassName;

                var type = NodeTypeManager.GetNodeType(className);
                string templateText;

                if (type == null)
                {
                    var interf = NodeTypeManager.GetNodeTypeInterface(className);

                    if (interf == null) continue;

                    templateText = Sugar.Resources.GetResourceText("CodeGeneration.Resources.CS_InterfaceTemplate.aspx", Assembly.GetExecutingAssembly());
                    templateText = Utility.FastReplace(templateText, "#INTERFACEID#", interf.ID.ToString(),
                        StringComparison.InvariantCultureIgnoreCase);
                }
                else
                {
                    if (!type.IsGenerated || type.ID.Equals(MedianamikTypes.BaseType) || type.ID.Equals(MedianamikTypes.BaseWebType)) continue;

                    templateText = Sugar.Resources.GetResourceText("CodeGeneration.Resources.CS_ViewTemplate.aspx", Assembly.GetExecutingAssembly());
                    templateText = Utility.FastReplace(templateText, "#TYPEID#", type.ID.ToString(),
                       StringComparison.InvariantCultureIgnoreCase);
                }

                templateText = Utility.FastReplace(templateText, "#PROVIDER#", Provider.Name,
                    StringComparison.InvariantCultureIgnoreCase);
                templateText = Utility.FastReplace(templateText, "#VIEW#", viewName,
                    StringComparison.InvariantCultureIgnoreCase);
                templateText = Utility.FastReplace(templateText, "#BASEPAGE#", Config.BasePage,
                    StringComparison.InvariantCultureIgnoreCase);

                var viewTemplate = new TurboTemplate(templateText, Language, Provider)
                   {
                       AddUsingBlock = addUsings,
                       TemplateName = "Medianamik Client View - " + Provider.Name + ": " + viewName
                   };

                foreach (PathElement item in Config.ReferenceAssemblies)
                {
                    viewTemplate.References.Add(item.Path);
                }

                var medianamikCore = Assembly.GetExecutingAssembly().Location;

                if (!viewTemplate.References.Contains(medianamikCore))
                {
                    viewTemplate.References.Add(medianamikCore);
                }

                if (writeFiles)
                {
                    var filePath = Path.Combine(pAbsoluteOutputPath, className + Language.FileExtension);
                    viewTemplate.OutputPath = filePath;
                }

                compiler.AddTemplate(viewTemplate);
            }

            if (compiler.Templates.Count > 0)
            {
                compiler.Run();
                var finalCode = new StringBuilder();

                var compiledTemplates = (from template in compiler.Templates
                                         select new
                                         {
                                             FinalCode = ConfigurationPropertyDefaultValue.CUSTOM_USING_STATEMENTS
                                                 + template.FinalCode,
                                             template.OutputPath
                                         }).ToList();

                compiledTemplates.ForEach(t => finalCode.Append(t.FinalCode));

                if (writeFiles)
                {
                    EnsureDirectoryExists(pAbsoluteOutputPath);
                    CleanDirectory(pAbsoluteOutputPath);
                    compiledTemplates.ForEach(t => SubSonic.Sugar.Files.CreateToFile(t.OutputPath, t.FinalCode));

                    GenerateUserProfile(pAbsoluteOutputPath);
                }

                return finalCode;
            }

            return new StringBuilder();
        }

        public void GenerateUserProfile(string absoluteOutputPath)
        {
            var writeFiles = !string.IsNullOrEmpty(absoluteOutputPath);

            if (Provider == null)
            {
                throw new CoreException("Unable to find CodeGenerationProvider. Check your web.Config File.");
            }

            var compiler = new TurboCompiler();
            var viewName = "vw_UserProfile";

            var className = "UserProfile";

            //TableSchema.Table view = DataService.GetSchema(viewName, Provider.Name, TableType.View);

            Provider.StripViewText = "vw_";
            Provider.ViewStartsWith = "vw_UserProfile";

            string templateText = Sugar.Resources.GetResourceText("CodeGeneration.Resources.CS_UserProfile.aspx", Assembly.GetExecutingAssembly());

            templateText = Utility.FastReplace(templateText, "#PROVIDER#", Provider.Name,
                    StringComparison.InvariantCultureIgnoreCase);
            templateText = Utility.FastReplace(templateText, "#VIEW#", viewName,
                StringComparison.InvariantCultureIgnoreCase);

            var viewTemplate = new TurboTemplate(templateText, Language, Provider)
            {
                TemplateName = "Medianamik Client View - " + Provider.Name + ": " + viewName
            };

            //foreach (PathElement item in Config.ReferenceAssemblies)
            //{
            //    viewTemplate.References.Add(item.Path);
            //}

            //var medianamikCore = Assembly.GetExecutingAssembly().Location;


            //viewTemplate.References.Add("Medianamik.UI.Web.Membership.Data");


            var filePath = Path.Combine(absoluteOutputPath, className + Language.FileExtension);
            viewTemplate.OutputPath = filePath;

            compiler.AddTemplate(viewTemplate);

            compiler.Run();

            EnsureDirectoryExists(absoluteOutputPath);
            //CleanDirectory(absoluteOutputPath);
            SubSonic.Sugar.Files.CreateToFile(compiler.Templates[0].OutputPath, 
                compiler.Templates[0].FinalCode);
        }

        private static void EnsureDirectoryExists(string pDirectory)
        {
            if (!Directory.Exists(pDirectory))
            {
                Directory.CreateDirectory(pDirectory);
            }
        }

        private static void CleanDirectory(string pDirectory)
        {
            foreach (var file in new DirectoryInfo(pDirectory).GetFiles("*.cs"))
            {
                file.Delete();
            }
        }

        public string GenerateMyProfileClass(string absoluteOutputPath)
        {
            var compiler = new TurboCompiler();
            var templateText = Sugar.Resources.GetResourceText("CodeGeneration.Resources.CS_MyProfile.aspx",
                 Assembly.GetExecutingAssembly());

            var myProfileCodeTemplate = new TurboTemplate(templateText, Language, Provider)
                   {
                       AddUsingBlock = false,
                       TemplateName = "CS_MyProfile.aspx",
                       OutputPath = Path.Combine(absoluteOutputPath, "MyProfile.cs")
                   };

            foreach (PathElement item in Config.ReferenceAssemblies)
            {
                myProfileCodeTemplate.References.Add(item.Path);
            }

            myProfileCodeTemplate.References.Add(Assembly.GetExecutingAssembly().Location);
            compiler.AddTemplate(myProfileCodeTemplate);
            compiler.Run();
            SubSonic.Sugar.Files.CreateToFile(compiler.Templates[0].OutputPath,
                compiler.Templates[0].FinalCode);

            return compiler.Templates[0].FinalCode;
        }


    }
}
